package com.kedacom.ctsp.authority.service.simple;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.kedacom.ctsp.authority.dao.UserJpaDao;
import com.kedacom.ctsp.authority.dto.UserImportDTO;
import com.kedacom.ctsp.authority.entity.Department;
import com.kedacom.ctsp.authority.entity.Person;
import com.kedacom.ctsp.authority.entity.Role;
import com.kedacom.ctsp.authority.entity.User;
import com.kedacom.ctsp.authority.service.DepartmentService;
import com.kedacom.ctsp.authority.service.PersonService;
import com.kedacom.ctsp.authority.service.RoleService;
import com.kedacom.ctsp.authority.service.UserService;
import com.kedacom.ctsp.authority.service.validator.PasswordStrengthValidator;
import com.kedacom.ctsp.authority.service.validator.UsernameValidator;
import com.kedacom.ctsp.authority.util.ImportExcelCommonUtil;
import com.kedacom.ctsp.authority.vo.AuthorityImportExcelResultVo;
import com.kedacom.ctsp.authz.entity.AuthPerson;
import com.kedacom.ctsp.authz.entity.AuthRole;
import com.kedacom.ctsp.authz.entity.AuthUser;
import com.kedacom.ctsp.authz.entity.Authentication;
import com.kedacom.ctsp.authz.exception.UnauthorizedException;
import com.kedacom.ctsp.lang.RandomUtil;
import com.kedacom.ctsp.lang.StringUtil;
import com.kedacom.ctsp.lang.exception.CommonException;
import com.kedacom.ctsp.lang.mapper.BeanMapper;
import com.kedacom.ctsp.lang.validate.ValidationException;
import com.kedacom.ctsp.office.excel.ExcelUtil;
import com.kedacom.ctsp.web.entity.CrudEntity;
import com.kedacom.ctsp.web.entity.DataStatus;
import com.kedacom.ctsp.web.service.StatusEnum;
import com.kedacom.ctsp.web.service.simple.AbstractCrudEntityService;
import com.kedacom.ctsp.web.validator.group.CreateGroup;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.kedacom.ctsp.authority.entity.SettingForType.PERSON;
import static java.util.stream.Collectors.groupingBy;

/**
 * 默认的用户服务实现
 *
 * @author
 * @since 3.0
 */
@Transactional(rollbackFor = Throwable.class)
@Service("authorityUserService")
@Slf4j
public class UserServiceImpl extends AbstractCrudEntityService<User, String> implements UserService {

    @Autowired
    private PasswordStrengthValidator passwordStrengthValidator;
    @Autowired
    private UsernameValidator usernameValidator;
    @Autowired(required = false)
    private PasswordEncoder passwordEncoder;
    @Autowired
    private PersonService personService;
    @Autowired
    private DepartmentService departmentService;
    @Autowired
    private ChargeRelationServiceImpl chargeRelationService;
    @Autowired
    private RoleService roleService;

    @Autowired
    private UserJpaDao userJpaDao;

    @Override
    @Transactional(readOnly = true)
    public User selectByUsername(String username) {
        if (org.apache.commons.lang3.StringUtils.isBlank(username)) {
            return null;
        }
        return createQuery().where(User.USERNAME, username).single();
    }

    @Override
    @Transactional(readOnly = true)
    public List<User> selectByPersionId(String personId) {
        if (personId == null) {
            return null;
        }
        return createQuery().where(User.PERSON_ID, personId).list();
    }

    @Override
    public List<User> get(List<String> id) {
        if (CollectionUtils.isEmpty(id)) {
            return new ArrayList<>();
        }
        return createQuery().where().in(CrudEntity.ID, id).listNoPaging();
    }

    @Override
    public String insert(User userEntity) {
        // 用户名合法性验证
        tryValidateProperty(usernameValidator, User.USERNAME, userEntity.getUsername());
        // 判断用户是否已经存在
        tryValidateProperty(null == selectByUsername(userEntity.getUsername()), User.USERNAME, "auth.user.username.duplicated");
        // 判断用户人员是否为空
        // tryValidateProperty(null != userEntity.getPerson(), User.PERSON_ID, "auth.user.persion.is.null");
        if (userEntity.getPerson() != null) {
            //判断用户人员是否存在
            tryValidateProperty(0 == selectByPersionId(userEntity.getPerson().getId()).size(), User.PERSON_ID, "auth.person.code.duplicated");
        }

        // 密码强度验证
        tryValidateProperty(passwordStrengthValidator, User.PASSWORD, userEntity.getPassword());
        // 默认状态
        if (userEntity.getStatus() == null) {
            userEntity.setStatus((int) DataStatus.STATUS_ENABLED);
        }
        //验证其他属性
        tryValidate(userEntity, CreateGroup.class);
        //密码MD5
        userEntity.setPassword(passwordEncoder.encode(userEntity.getPassword()));
        //创建用户
        String userId = getDao().insert(userEntity);

        return userEntity.getId();
    }

    @Override
    public String patch(String userId, User entity) {
        entity.setId(userId);
        User oldUser = get(userId);

        // 用户名是否改变
        if(oldUser==null){
            tryValidateProperty(null == oldUser, "", "用户不存在");
        }
        if (!oldUser.getUsername().equals(entity.getUsername())) {
            tryValidateProperty(null == selectByUsername(entity.getUsername()), User.USERNAME, "auth.user.username.duplicated");
        }
        // 修改密码
        if (StringUtils.hasLength(entity.getPassword())) {
            //密码强度验证
            tryValidateProperty(passwordStrengthValidator, User.PASSWORD, entity.getPassword());
            //密码MD5
            entity.setPassword(passwordEncoder.encode(entity.getPassword()));
        } else {
            entity.setPassword(oldUser.getPassword());
        }
        // 更新角色置空
        if (entity.getRoles() != null && entity.getRoles().size() == 0) {
            oldUser.setRoles(null);
        }
        return super.patch(userId, entity);
    }

    /**
     * 检测登录用户是否可以登录
     *
     * @param userEntity
     * @return
     */
    @Override
    public boolean checkLoginUserStatus(User userEntity) {
        if (userEntity.getStatus() != null && StatusEnum.ENABLE.ordinal() == userEntity.getStatus()) {
            return true;
        }
        return false;
    }

    /**
     * 修改密码
     *
     * @param userId
     * @param oldPassword
     * @param newPassword
     */
    @Override
    public String updatePassword(String userId, String oldPassword, String newPassword) {
        User userEntity = get(userId);
        assertNotNull(userEntity);

        if (!passwordEncoder.matches(oldPassword, userEntity.getPassword())) {
            throw new ValidationException("auth.user.password.old.error", "password");
        }
        newPassword = passwordEncoder.encode(newPassword);
        userEntity.setPassword(newPassword);
        update(userEntity);
        return userEntity.getId();
    }

    /**
     * 重置密码
     *
     * @param userId
     * @param newPassword
     */
    @Override
    public String resetPassword(String userId, String newPassword) {
        User userEntity = get(userId);
        assertNotNull(userEntity);

        passwordStrengthValidator.validate(newPassword);
        newPassword = passwordEncoder.encode(newPassword);
        userEntity.setPassword(newPassword);
        update(userEntity);
        return userEntity.getId();
    }

    /**
     * 1    人员编号验证错误
     * 该人员编号不存在
     * 2    人员编号和人员姓名不一致
     * 该人员编号和人员姓名不一致
     * 3    人员编号和人员归属部门ID不符
     * 该人员编号和所属部门不对应
     * 4    用户名不唯一（验证与已存在用户名是否重复）
     * 用户名和已有用户名重复
     * 5    用户名不唯一（验证导入表格内是否重复）
     * 本次导入的用户名存在重复用户名
     * 6    用户名不符合系统规范【规范待定】
     * 用户名不符合系统规范
     * 7    权限角色ID不存在
     * 【角色ID】权限角色不存在
     *
     * @param userDtos
     * @return
     */
    @Override
    public List<? extends UserImportDTO> importUsers(List<? extends UserImportDTO> userDtos) {
        saveImportUsers(userDtos);
        return userDtos;
    }

    @Override
    public AuthorityImportExcelResultVo importUsers(MultipartFile file, HttpServletRequest request) throws Exception {
        Locale locale = LocaleContextHolder.getLocale();
        // 获取excel的数据 分为中文版和国际版
        List<? extends UserImportDTO> userDtos;
        if (locale.getLanguage().equalsIgnoreCase("zh")) {
            userDtos = ExcelUtil.read2Bean(file.getInputStream(), UserImportDTO.headerMapper, UserImportDTO.class);
        } else {
            userDtos = ExcelUtil.read2Bean(file.getInputStream(), UserImportDTO.headerMapper_en, UserImportDTO.class);
        }
        // 真正的导入 保存数据
        saveImportUsers(userDtos);
        // 记录日志
        StringBuilder message = new StringBuilder();
        String serialNo = RandomUtil.randomString(32);
        int succCount = 0;
        int failCount = 0;

        for (UserImportDTO vo : userDtos) {
            if (CollectionUtils.isEmpty(vo.getErrors())) {
                ImportExcelCommonUtil.setMessage(message, vo.getRownum(), vo.getErrors());
                succCount++;

            } else {
                ImportExcelCommonUtil.setMessage(message, vo.getRownum(), vo.getErrors());
                failCount++;

            }
        }

        return ImportExcelCommonUtil.setImportExcelResult(serialNo, succCount, failCount, message);
    }

    /**
     * 组装需要的用户数据
     *
     * @param userDtos
     */
    private Map<String, List<User>> setImportUserMapInfo(List<? extends UserImportDTO> userDtos) {
        //用户名唯一
        Set<String> usernames = userDtos.stream().map(UserImportDTO::getUsername).collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(usernames)) {
            List<User> users = createQuery().in(User.USERNAME, usernames).listNoPaging();
            if (CollectionUtils.isNotEmpty(users)) {
                return users.stream().collect(groupingBy(User::getUsername));
            }
        }
        return Maps.newHashMap();
    }


    /**
     * 组装需要的人员检测数据
     *
     * @param userDtos
     * @return
     */
    private Map<String, List<Person>> setImportPersonMapInfo(List<? extends UserImportDTO> userDtos) {
        Set<String> personCodes = userDtos.stream().filter(
                u -> u.getPersonCode() != null
        ).map(UserImportDTO::getPersonCode).collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(personCodes)) {
            List<Person> persons = personService.createQuery().in(Person.CODE, personCodes).listNoPaging();

            // 检测人员编号
            return persons.stream().collect(groupingBy(Person::getCode));
        }
        return Maps.newHashMap();
    }

    /**
     * 组装需要的用户角色数据
     *
     * @param userDtos
     * @return
     */
    private Map<String, List<Role>> setImportRoleMapInfo(List<? extends UserImportDTO> userDtos) {
        Set<String> roleSigns = userDtos.stream().filter(
                u -> u.getRoleSign() != null
        ).map(UserImportDTO::getRoleSign).flatMap(line -> Stream.of(line.split(","))).collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(roleSigns)) {
            List<Role> roles = roleService.createQuery().in(Role.SIGN, roleSigns).listNoPaging();

            //检测用户角色
            return roles.stream().collect(groupingBy(Role::getSign));
        }

        return Maps.newHashMap();
    }

    /**
     * 组装需要的部门检测数据
     *
     * @param userDtos
     * @return
     */
    private Map<String, List<Department>> setImportDeptMapInfo(List<? extends UserImportDTO> userDtos) {
        if (CollectionUtils.isNotEmpty(userDtos)) {
            Set<String> deptCodes = userDtos.stream().map(UserImportDTO::getDepartmentCode).collect(Collectors.toSet());
            if (CollectionUtils.isNotEmpty(deptCodes)) {
                List<Department> depts = departmentService.createQuery().in(Department.CODE, deptCodes).listNoPaging();
                return depts.stream().collect(groupingBy(Department::getCode));
            }
        }
        return Maps.newHashMap();
    }


    /**
     * 检测
     *
     * @param userDtos
     * @param personMap
     * @param deptMap
     * @param userMap
     * @return
     */
    private List<? extends UserImportDTO> checkImportErrors(List<? extends UserImportDTO> userDtos,
                                                            Map<String, List<Person>> personMap,
                                                            Map<String, List<Department>> deptMap,
                                                            Map<String, List<User>> userMap,
                                                            Map<String, List<Role>> roleMap
    ) {
        for (UserImportDTO ui : userDtos) {
            // 该人员编号不存在
            List<Person> ps = personMap.get(ui.getPersonCode());
            if (CollectionUtils.isEmpty(ps)) {
                ui.getErrors().add(StringUtil.format("auth.person.code.notexist"));
            } else {
                // 人员编号和人员姓名不一致
                Person p = ps.get(0);
                if (!p.getName().equals(ui.getPersonName())) {
                    ui.getErrors().add(StringUtil.format("auth.person.code_and_name.not_match"));
                } else {
                    ui.setPersonId(p.getId());
                }
            }
            // 人员编号和人员归属部门ID不符
            List<Department> ds = deptMap.get(ui.getDepartmentCode());
            if (CollectionUtils.isEmpty(ds)) {
                ui.getErrors().add(StringUtil.format("auth.person.code_and_department.not_match"));
            }
            //用户名不唯一（验证与已存在用户名是否重复）
            if (userMap != null && CollectionUtils.isNotEmpty(userMap.keySet())) {
                List<User> userEntities = userMap.get(ui.getUsername());
                if (CollectionUtils.isNotEmpty(userEntities)) {
                    ui.getErrors().add(StringUtil.format("auth.user.username.duplicated"));
                }
            }
            //判断角色是否为空
            if (StringUtils.isEmpty(ui.getRoleSign())) {
                ui.getErrors().add(StringUtil.format("auth.role.sign.isnull"));
            }
            //判断角色是否存在
            List<String> rs = Arrays.stream(ui.getRoleSign().split(",")).filter(r -> roleMap.containsKey(r)).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(rs)) {
                ui.getErrors().add(StringUtil.format("auth.role.sign.notexist"));
            }

//                                * 5    用户名不唯一（验证导入表格内是否重复）
//                                * 6    用户名不符合系统规范【规范待定】
//                                * 7    权限角色ID不存在

        }
        return userDtos;
    }


    private String getDeptIdByUser(User user) {
        if (user == null || user.getPerson() == null || user.getPerson().getDepartment() == null) {
            return null;
        }
        return user.getPerson().getDepartment().getId();
    }

    /**
     * 根据user获取deptid
     *
     * @param userId 用户id
     * @return department id
     */
    @Override
    public String getDeptIdByUserId(String userId) {
        User user = get(userId);
        return getDeptIdByUser(user);
    }

    @Override
    public AuthPerson getAuthPersonByUser(AuthUser authUser) {
        String userId = authUser.getId();
        log.info("getAuthPersonByUser userId[{}]", authUser.getId());
        User user = get(userId);
        //通过user信息获取部门id
        String departmentId = getDeptIdByUser(user);

        AuthPerson authPerson = null;
        if (user.getPerson() != null) {
            // 转换person
            authPerson = BeanMapper.deepMap(user.getPerson(), AuthPerson.class);
            //获取部门code和以及下级部门的code
            authPerson.setCascadedDeptCodes(departmentService.getCascadedDepartmentCodes(departmentId));
            // 分管和级联部门
            authPerson.setCascadedAndChargedDeptCodes(departmentService.getCascadedAndChargedDepartmentCodes(user.getPerson().getId(), departmentId));

            //分管人员
            authPerson.setChargedUsers(getChargedPeople(userId));

            if (user.getPerson().getDepartment() != null) {
                authPerson.setDeptCode(user.getPerson().getDepartment().getCode());
                authPerson.setDeptName(user.getPerson().getDepartment().getName());
            }
        }
        return authPerson;
    }


    /**
     * 加载用户的角色
     *
     * @return 用户的角色
     */
    @Override
    public Set<AuthRole> getAuthRolesByUser(String uid) {

        Set<Role> roles = get(uid).getRoles();
        if (CollectionUtils.isEmpty(roles)) {
            return Sets.newHashSet();
        }
        Set<AuthRole> authRoles = new HashSet<>();
        roles
                .stream()
                //过滤无效的角色
                .forEach(r -> {

                    if (r.getStatus() != null && StatusEnum.ENABLE.ordinal() == r.getStatus()) {
                        AuthRole authRole = new AuthRole();
                        authRole.setId(r.getId());
                        authRole.setName(r.getName());
                        authRole.setSign(r.getSign());
                        authRoles.add(authRole);
                    }

                });

        BeanMapper.deepCopy(roles, authRoles);
        return authRoles;
    }

    @Override
    public Map<String, Set<String>> getAuthRolesByUser(Collection<String> uids) {
        //<userId,Set<RoleId>>
        Map<String, Set<String>> userRoleIdsMap = new HashMap<>();

        //根据userId，获取userId和roleId object[0]:userId;object[1]:roleId
        List<Object[]> userRoleIds = userJpaDao.getUserRoleIds(new ArrayList<>(uids), StatusEnum.ENABLE.ordinal());
        for (Object[] object : userRoleIds) {

            String userId = (String) object[0];
            String roleId = (String) object[1];
            Set<String> roleIdSet = userRoleIdsMap.get(userId);
            if (roleIdSet == null) {
                roleIdSet = new HashSet<>();
                userRoleIdsMap.put(userId, roleIdSet);
            }

            roleIdSet.add(roleId);
        }

        return userRoleIdsMap;
    }

    @Override
    public Map<String, String> getDeptIdByUserIds(Collection<String> userIds) {

        //<userId，deptId>
        Map<String, String> userDeptMap = new HashMap<>();

        //根据userId，获取userId和deptId object[0]:userId;object[1]:deptId
        List<Object[]> userDeptIds = userJpaDao.getUserDeptIds(new ArrayList<>(userIds));
        for (Object[] object : userDeptIds) {

            String userId = (String) object[0];
            String deptId = (String) object[1];
            userDeptMap.put(userId, deptId);
        }
        return userDeptMap;
    }

    /**
     * 用户自身分管的用户
     *
     * @return user ids
     */
    private Set<String> getChargedPeople(String userId) {
        Person person = get(userId).getPerson();
        if (person == null) {
            return Collections.emptySet();
        }
        String personId = person.getId();

        // 部门分管的用户
        Set<String> personIds = chargeRelationService.getChargeIds(PERSON.ordinal(), personId, PERSON.ordinal());
        if (CollectionUtils.isEmpty(personIds)) {

            return Collections.emptySet();
        }

        //通过personId获取用户Id
        return new HashSet<>(userJpaDao.getUserIdsByPersonIds(new ArrayList<>(personIds)));
    }

    private void saveImportUsers(List<? extends UserImportDTO> userDtos) {
        if (CollectionUtils.isNotEmpty(userDtos)) {
            Authentication authentication = Authentication.current().orElseThrow(UnauthorizedException::new);
            // 获取检测导入数据的信息
            Map<String, List<Person>> personMap = setImportPersonMapInfo(userDtos);
            Map<String, List<Department>> deptMap = setImportDeptMapInfo(userDtos);
            Map<String, List<User>> userMap = setImportUserMapInfo(userDtos);
            Map<String, List<Role>> roleMap = setImportRoleMapInfo(userDtos);

            // 预先检测导入错误
            checkImportErrors(userDtos, personMap, deptMap, userMap, roleMap);

            // 真正的导入操作
            int i = 2;
            for (UserImportDTO ui : userDtos) {
                if (CollectionUtils.isEmpty(ui.getErrors())) {
                    try {
                        User user = BeanMapper.newInstanceSilently(User.class);
                        user.setUsername(ui.getUsername());
                        user.setPassword(ui.getPassword());
                        user.setName(ui.getPersonName());

                        Person person = BeanMapper.newInstanceSilently(Person.class);
                        person.setId(ui.getPersonId());

                        user.setPerson(person);

                        //role
                        List<String> listSigns = Lists.newArrayList(ui.getRoleSign().split(","));
                        List<Role> listRole = roleService.createQuery().in(Role.SIGN, listSigns).listNoPaging();
                        if (CollectionUtils.isNotEmpty(listRole)) {
                            user.setRoles(listRole.stream().collect(Collectors.toSet()));
                        }

                        if (authentication.getPerson() != null) {

                            user.setCreateDeptCode(authentication.getPerson().getDeptCode());

                        }
                        if (authentication.getUser() != null) {

                            user.setCreatorCode(authentication.getUser().getUsername());
                        }
                        String userId = insert(user);
                        ui.setUserId(userId);
                    } catch (CommonException e) {
                        logger.error("excel.import.error", e);
                        ui.getErrors().add(e.getMessage());
                    } catch (Exception e) {
                        logger.error("excel.import.error", e);
                        ui.getErrors().add(StringUtil.format("excel.import.error") + " : " + e.getMessage());
                    }
                }
                ui.setRownum(i++);
            }
        }
    }

}
